package com.gitee.wsl.jvm.math

import com.gitee.wsl.math.Math
import java.math.BigDecimal
import java.math.BigInteger
import kotlin.math.pow
import kotlin.math.sqrt


val ERROR_BIG_DECIMAL: BigDecimal = BigDecimal.valueOf(-1L)
val ERROR_BIG_INTEGER = BigInteger.valueOf(-1L)
val BIG_DECIMAL: BigDecimal = BigDecimal.ZERO
val BIG_INTEGER = BigInteger.ZERO

/**
 * 加法运算
 * @param v1
 * @param v2
 * @return
 */
fun Math.add(v1: Float, v2: Float, mHighPrecision:Boolean=false): Float {
    return if (!mHighPrecision) {
        v1 + v2
    } else {
        val bgNum1 = v1.toBigDecimal()
        val bgNum2 = v2.toBigDecimal()
        (bgNum1+bgNum2).toFloat()
    }
}

/**
 * 减法运算
 * @param v1
 * @param v2
 * @return 运算结果
 */
fun Math.sub(v1: Float, v2: Float, mHighPrecision:Boolean=false): Float {
    return if (!mHighPrecision) {
        v1 - v2
    } else {
        val bgNum1 = v1.toBigDecimal()
        val bgNum2 = v2.toBigDecimal()
        (bgNum1-bgNum2).toFloat()
    }
}

/**
 * 乘法运算
 * @param v1
 * @param v2
 * @return 运算结果
 */
fun Math.mul(v1: Float, v2: Float, mHighPrecision:Boolean=true): Float {
    return if (!mHighPrecision) {
        v1 * v2
    } else {
        val bgNum1 = v1.toBigDecimal()
        val bgNum2 = v2.toBigDecimal()
        //bgNum1.multiply(bgNum2).toFloat()
        (bgNum1*bgNum2).toFloat()
    }
}


/**
 * 除法运算,当除不尽时，精确到小数点后scale位
 * @param v1
 * @param v2
 * @param scale
 * @return 运算结果
 */
fun Math.div(v1: Float, v2: Float, scale: Int=2, mHighPrecision:Boolean=true): Float {
    require(scale >= 0) { "The scale must be a positive integer or zero" }
    if (v2.compareTo(0.0f) == 0) return 0.0f
    return if (!mHighPrecision) {
        v1 / v2
    } else {
        val bgNum1 = v1.toBigDecimal()
        val bgNum2 = v2.toBigDecimal()
        bgNum1.divide(bgNum2, scale, BigDecimal.ROUND_HALF_UP).toFloat()
    }
}

/**
 * 四舍五入到小数点后scale位
 * @param v
 * @param scale
 * @return
 */
fun Math.round(v: Float, scale: Int): Float {
    require(scale >= 0) { "The scale must be a positive integer or zero" }
    val bgNum1 = v.toBigDecimal()
    val bgNum2 = BigDecimal("1")
    return bgNum1.divide(bgNum2, scale, BigDecimal.ROUND_HALF_UP).toFloat()
    // return b.setScale(scale, BigDecimal.ROUND_HALF_UP).floatValue();
}

fun Math.add(v1: Double, v2: Double, mHighPrecision:Boolean=true): Double {
    return if (!mHighPrecision) {
        v1 + v2
    } else {
        val bgNum1 = v1.toBigDecimal()
        val bgNum2 = v2.toBigDecimal()
        //bgNum1.add(bgNum2).toDouble()
        (bgNum1+bgNum2).toDouble()
    }
}

fun Math.sub(v1: Double, v2: Double, mHighPrecision:Boolean=true): Double {
    return if (!mHighPrecision) {
        v1 - v2
    } else {
        val bgNum1 = v1.toBigDecimal()
        val bgNum2 = v2.toBigDecimal()
        //bgNum1.subtract(bgNum2).toDouble()
        (bgNum1-bgNum2).toDouble()
    }
}


/**
 * 除法计算
 * @param v1
 * @param v2
 * @param scale    指定保留精度
 * @return
 */
fun Math.div(v1: Double, v2: Double, scale: Int=2, mHighPrecision:Boolean=true): Double {
    require(scale >= 0) { "The scale must be a positive integer or zero" }
    if (v2.compareTo(0.0) == 0) return 0.0
    return if (!mHighPrecision) {
        v1 / v2
    } else {
        val bgNum1 = v1.toBigDecimal()
        val bgNum2 = v2.toBigDecimal()
        bgNum1.divide(bgNum2, scale, BigDecimal.ROUND_HALF_UP).toDouble()
    }
}

/**
 * 乘法运算
 * @param v1
 * @param v2
 * @return 运算结果
 */
fun Math.mul(v1: Double, v2: Double, mHighPrecision:Boolean=true): Double {
    return if (!mHighPrecision) {
        v1 * v2
    } else {
        val bgNum1 = v1.toBigDecimal()
        val bgNum2 = v2.toBigDecimal()
        //bgNum1.multiply(bgNum2).toDouble()
        (bgNum1*bgNum2).toDouble()
    }
}

/**
 * 四舍五入到小数点后scale位
 * @param v
 * @param scale
 * @return
 */
fun Math.round(v: Double, scale: Int): Double {
    require(scale >= 0) { "The scale must be a positive integer or zero" }
    val bgNum1 = v.toBigDecimal()
    val bgNum2 = BigDecimal("1")
    return bgNum1.divide(bgNum2, scale, BigDecimal.ROUND_HALF_UP).toDouble()
    // return b.setScale(scale, BigDecimal.ROUND_HALF_UP).floatValue();
}

//函数来自LnChart，位置算法现在到处都是，可以考虑优化下。[各轴，定制线]
fun Math.getLnPlotXValPosition(
    plotScreenWidth: Float, plotAreaLeft: Float,
    xValue: Float, maxValue: Float, minValue: Float
): Float {
    // 对应的X坐标
    val maxminRange = sub(maxValue, minValue)
    val xScale = div(sub(xValue, minValue), maxminRange)
    return mul(plotScreenWidth, xScale)
}

object Erf {
    private const val MIN_APPROX = 17

    private var denominators = mutableMapOf<Int, Double>()

    private fun tailor(x: Double, approx: Int): Double {
        var result = 0.0
        var member: Double

        (0..approx)
            .forEach { n ->
                member = x.pow(2 * n + 1)

                if (n > 0) {
                    member /= denominator(n)

                    if (n % 2 == 1) {
                        member = -member
                    }
                }

                result += member
            }

        return result
    }

    private fun denominator(n: Int): Double {
        if (!denominators.contains(n)) {
            denominators[n] = factorial(n).toDouble() * (2 * n + 1).toDouble()
        }

        return denominators[n]!!
    }

    fun calc(x: Double, approx: Int = MIN_APPROX): Double {
        if (approx < MIN_APPROX) {
            throw IllegalArgumentException(
                "approx $approx was given, but it must be not less than $MIN_APPROX, " +
                        "otherwise calculation precision is too low"
            )
        }

        return 2 / sqrt(Math.PI) * tailor(x, approx)
    }

    fun factorial(num: Int): BigInteger {
        if (num < 0) {
            throw IllegalArgumentException("Only positive number can be passed, $num was given")
        }

        var result = BigInteger.ONE

        if (num >= 2) {
            (2..num).forEach { i -> result *= i.toBigInteger() }
        }

        return result
    }
}